Skip to content

BUILD-11310 Add report-ci-metrics action#298

Merged
mikolaj-matuszny-ext-sonarsource merged 17 commits into
masterfrom
BUILD-11310-report-ci-insights
Jun 15, 2026
Merged

BUILD-11310 Add report-ci-metrics action#298
mikolaj-matuszny-ext-sonarsource merged 17 commits into
masterfrom
BUILD-11310-report-ci-insights

Conversation

@mikolaj-matuszny-ext-sonarsource

@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What

New report-ci-metrics composite action (M4.3). A repository adds it as one dedicated job; it aggregates per-job CI metrics across the workflow run and posts a single sticky PR comment with a compact headline over folded detail.

Ticket: BUILD-11310 · Epic: BUILD-11068

Tested in https://github.com/SonarSource/sonar-dummy/pull/619

Renamed from report-ci-insightsreport-ci-metrics: the action reports metrics, it doesn't interpret them; "metrics" matches the hook's ## CI Metrics branding. "insights" is reserved for the planned trend/AI work.

How it gets the data (the interesting part)

Both "obvious" transports were empirically ruled out: the hook can't upload an artifact (no runtime token in the hook env — BUILD-11309), and job summaries aren't readable cross-job via any token API. The working transport: the report job (needs: [all]) downloads each completed sibling's job log via GET /repos/{r}/actions/jobs/{id}/logs (GITHUB_TOKEN + actions: read) and recovers the metrics JSON the runner hook prints to stdout, wrapped in sentinels (producer PRs: infra#422, this-repo#297). Proven end-to-end in a spike.

Usage

jobs:
  build: ...
  test: ...
  report-ci-metrics:
    needs: [build, test]
    if: always() && github.event_name == 'pull_request'
    runs-on: sonar-xs
    permissions:
      actions: read           # download sibling job logs
      pull-requests: write    # post/update the sticky comment
    steps:
      - uses: SonarSource/ci-github-actions/report-ci-metrics@v1

Behaviour

  • Headline (always visible): run totals (CPU-s, worst peak memory + job, network, cache) + a flags line only when something is wrong (OOM kills / CPU throttling).
  • Folded detail: per-job table + cache table in <details>. Folding rules: columns with no data are dropped; the Flags column/line appears only when flags exist; the cache fold renders only when a job reported cache.
  • Sticky: one comment, matched by a hidden marker, updated idempotently on re-runs.
  • Fail-open: any error → ::warning:: + exit 0; never fails the consumer workflow. Skips the report job itself, non-completed jobs, log-download failures, jobs without metrics, and corrupt-JSON records.

@sonarqubecloud

sonarqubecloud Bot commented Jun 12, 2026

Copy link
Copy Markdown

Agentic Analysis: Early Results

Agentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action.

3 issue(s) found across 1 file(s):

Rule File Line Message
shelldre:S1192 spec/report-ci-metrics_spec.sh 321 Define a constant instead of using the literal '%s\t%s\n' 16 times.
shelldre:S1192 spec/report-ci-metrics_spec.sh 383 Define a constant instead of using the literal 'PATCHED 999' 5 times.
shelldre:S1192 spec/report-ci-metrics_spec.sh 384 Define a constant instead of using the literal 'POSTED' 6 times.

Analyzed by SonarQube Agentic Analysis in 3.5 s

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jun 12, 2026

Copy link
Copy Markdown

BUILD-11310

Comment thread report-ci-insights/lib.sh Outdated
@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource marked this pull request as draft June 12, 2026 13:25
@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource marked this pull request as ready for review June 15, 2026 10:49
mikolaj-matuszny-ext-sonarsource added a commit that referenced this pull request Jun 15, 2026
Author-influenced values (job name, cache key, backend) recovered via jq -r
could carry embedded newlines or HTML (e.g. </details>) that break the markdown
table or inject markup into the write-scoped sticky comment. Centralize cell
sanitization in _rci_md_cell: escape pipes, collapse CR/LF, and neutralize angle
brackets. Addresses Gitar review finding on PR #298.
@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource changed the title BUILD-11310 Add report-ci-insights action (per-PR CI metrics comment) BUILD-11310 Add report-ci-metrics action (per-PR CI metrics comment) Jun 15, 2026
@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource changed the title BUILD-11310 Add report-ci-metrics action (per-PR CI metrics comment) BUILD-11310 Add report-ci-metrics action Jun 15, 2026
Comment thread report-ci-metrics/lib.sh
Comment thread report-ci-metrics/README.md Outdated
Comment thread report-ci-metrics/lib.sh
Comment thread report-ci-metrics/README.md Outdated
Collapse four multi-line shell constructs onto single physical lines so
kcov attributes their execution correctly, eliminating line-attribution
artifacts that showed continuation lines as uncovered. Behavior is
unchanged and all 34 shellspec tests remain green.

- collect_job_metrics: join gh api --paginate -q onto one line
- _rci_cpu_cell: collapse the multi-line jq program to a single filter
- render_cache_fold: build the cache row with a $'\n' literal instead of
  an embedded newline in the assignment
- upsert_comment: join gh api --paginate -q onto one line

lib.sh coverage 92.74% -> 100.00% (168/168).
Author-influenced values (job name, cache key, backend) recovered via jq -r
could carry embedded newlines or HTML (e.g. </details>) that break the markdown
table or inject markup into the write-scoped sticky comment. Centralize cell
sanitization in _rci_md_cell: escape pipes, collapse CR/LF, and neutralize angle
brackets. Addresses Gitar review finding on PR #298.
… compat

In bash 4.3+, a bare & in the replacement of ${var//pat/repl} expands to the
matched text, so <//&lt; produced '<lt;' instead of '&lt;' on CI (bash 5.x)
while passing on local bash 3.2. Escape the & (\&lt;/\&gt;) so the entity is
literal on all bash versions.
The action reports CI metrics (CPU/memory/disk/network/cache); it does not
interpret them. 'metrics' matches the feature's branding (the hook's
'## CI Metrics' summary) and is more honest than 'insights', which is reserved
for the planned trend/AI work. Renames the dir, script, spec, and updates
sonar.sources + the kcov include-pattern.
Consolidate the action's documentation into the root README's 'Actions
provided' section, matching every other action in the repo (no sub-folder
READMEs elsewhere). Drop the separate report-ci-metrics/README.md. Also
reword the fail-open note ('the workflow' per review).
Follow-up to the README move: the previous commit only captured the
sub-folder README deletion (root edits were left unstaged by the pre-commit
stash). This adds the consolidated section + actions-list entry.
@sonarqubecloud

Copy link
Copy Markdown

@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource merged commit 657e445 into master Jun 15, 2026
17 checks passed
@mikolaj-matuszny-ext-sonarsource mikolaj-matuszny-ext-sonarsource deleted the BUILD-11310-report-ci-insights branch June 15, 2026 13:43
@gitar-bot

gitar-bot Bot commented Jun 15, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Introduces the report-ci-insights composite action to aggregate and report workflow metrics via sticky PR comments. Consider neutralizing Markdown link and image syntax within table cells to prevent potential injection vulnerabilities.

✅ 2 resolved
Security: Rendered metric fields escape only pipes, not newlines/HTML

📄 report-ci-insights/lib.sh:193-196 📄 report-ci-insights/lib.sh:247-256
render_table escapes | in the job name (line 196) and render_cache_fold escapes | in the cache key and backend (lines 254-255), but no other characters are sanitized. These values are produced by jq -r, which converts JSON escapes into literal newlines, and they are emitted directly into the markdown table rows of the sticky PR comment.

The metrics JSON is recovered from sibling job stdout (a runner hook the producing job prints), so a field value such as a cache key of "foo malicious row" or one containing HTML like </details> will (a) break the markdown table layout, and (b) inject attacker-influenced markdown/HTML into a comment posted with pull-requests: write. Pipe-escaping alone does not prevent this. Risk is limited because forked-PR GITHUB_TOKEN is read-only, but for same-repo PRs the report is posted with write permission.

Suggested fix: strip/escape newlines (and ideally other control chars) in the same place pipes are escaped, e.g. replace embedded newlines with a space or <br> before building the row. Apply to name, key, and backend.

Security: Markdown link/image syntax not neutralized in table cells

📄 report-ci-metrics/lib.sh:36-45 📄 report-ci-metrics/lib.sh:183 📄 report-ci-metrics/lib.sh:240-241
_rci_md_cell now escapes pipes, CR/LF, and angle brackets (<,>), which closes the previously-resolved HTML/newline gap. However it does not neutralize Markdown link/image syntax. Author-influenced values that reach cells — notably cache key, backend, and job name (derived from branch names, matrix values, cache config) — can still contain ![alt](https://attacker/pixel.png) or [text](https://...). Because the comment is posted with pull-requests: write, a value like a branch or cache key set to ![](https://evil/track.png) would render as an auto-loading image (tracking pixel / IP leak) when a maintainer views the PR. The threat model is limited (the data originates from the PR author's own repo configuration), so this is minor, but it is a different vector than the angle-bracket escaping already added.

Suggested fix: also escape the Markdown-significant leading characters in _rci_md_cell, e.g. backtick, [, ], and !, or wrap each cell value in backticks (code span) so Markdown image/link syntax is rendered literally.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants